// 第 3 步：补充常量与辅助函数

#pragma once

// step 1:
// 先写出头文件常用的 #pragma once、依赖的系统头文件、命名空间声明。这一步暂时只保留最基础的框架
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <netdb.h>
#include <netinet/tcp.h>
#include <string>
#include <sstream>
#include <fcntl.h>

namespace Common {

// step 2:
// 这个结构体封装创建 socket 所需的参数，同时提供 toString() 便于记录日志。
struct SocketCfg {
  std::string ip_;
  std::string iface_;
  int port_ = -1;
  bool is_udp_ = false;
  bool is_listening_ = false;
  bool needs_so_timestamp_ = false;

  auto toString() const {
    std::stringstream ss;
    ss << "SocketCfg[ip:" << ip_
       << " iface:" << iface_
       << " port:" << port_
       << " is_udp:" << is_udp_
       << " is_listening:" << is_listening_
       << " needs_SO_timestamp:" << needs_so_timestamp_
       << "]";
    return ss.str();
  }
};

constexpr int MaxTCPServerBacklog = 1024;

inline auto getIfaceIP(const std::string &iface) -> std::string {
  char buf[NI_MAXHOST] = {'\0'};
  ifaddrs *ifaddr = nullptr;

  if (getifaddrs(&ifaddr) != -1) {
    for (ifaddrs *ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
      if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET && iface == ifa->ifa_name) {
        getnameinfo(ifa->ifa_addr, sizeof(sockaddr_in), buf, sizeof(buf), NULL, 0, NI_NUMERICHOST);
        break;
      }
    }
    freeifaddrs(ifaddr);
  }

  return buf;
}

// step3:
// 加入最大监听队列 MaxTCPServerBacklog，以及 getIfaceIP / setNonBlocking / disableNagle 等基础工具函数。它们将被后面的 createSocket 使用。
inline auto setNonBlocking(int fd) -> bool {
  const auto flags = fcntl(fd, F_GETFL, 0);
  if (flags & O_NONBLOCK)
    return true;
  return (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != -1);
}

inline auto disableNagle(int fd) -> bool {
  int one = 1;
  return (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<void *>(&one), sizeof(one)) != -1);
}

inline auto setSOTimestamp(int fd) -> bool {
  int one = 1;
  return (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, reinterpret_cast<void *>(&one), sizeof(one)) != -1);
}

inline auto join(int fd, const std::string &ip) -> bool {
  const ip_mreq mreq{{inet_addr(ip.c_str())}, {htonl(INADDR_ANY)}};
  return (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) != -1);
}

} // namespace Common